package com.bnq.logc.config

import android.content.Context
import android.util.Log
import com.bnq.logc.logger.Logger
import com.bnq.logc.utils.DateUtil
import com.bnq.logc.utils.DeviceUtil
import com.bnq.logc.utils.FileUtil
import com.cj.clog.Clog
import java.io.File
import java.util.concurrent.TimeUnit

class LogcConfig {
    companion object{
        private const val DAYS :Long = 24 * 60 * 60 * 1000 //天
        private const val MONTH :Long = 30 * DAYS //月
        private const val M :Long = 1024 * 1024 //M
        private const val DEFAULT_DAY :Long = 7 * DAYS //默认删除天数
        private const val DEFAULT_MONTH :Long = 3 * 12 * MONTH //默认删除月数
        private const val DEFAULT_FILE_SIZE :Long = 30 * M

        private const val PARENT_DIR = "logc/log"

        inline fun build(block: Builder.() -> Unit) = Builder().apply(block).build()
    }

    var mMaxFile = DEFAULT_FILE_SIZE //删除文件最大值
    var mDay = DEFAULT_MONTH //删除天数
    var mPathPath :String = "" //file文件路径

    var mCustomHost:String = "" //上传服务器的Host
    var mUploadPublicKey:String = ""
    var mUploadPrivateKey:String = ""
    var mBucketName:String = ""
    val mCloudParentDir: String = "" //上传服务器的文件路径
    var mUniqueDeviceId:String = "" //设备唯一序列码
    var mIsUploadLog:Boolean = false //是否上传本地日志到服务器
    var mIsSendLog:Boolean = false //是否发送日志到后台服务
    var mSendUrl:String = "" ////上传后台的url
    var mDefaultSendBodyParamFun:(()->Map<String,Any?>?)? = null

    var mStorageDays:Long = 30 //默认存储30天,超过30天会被删除不管是否上传结束
    var mLowMemoryByte:Long = 200 * M //内存低于200M，删除${mStorageDays}天内的已经上传的日志文件


    var mContent:Context
    var mIsPrintLog = false
    var loggers: List<Logger>? = null

    var mIsDebug:Boolean = false

    //是否开启上传日志之前保存的简单日志文件
    var mIsOpenSimpleCacheLogUpload = false

    var mUploadScheduleConfig = UploadScheduleConfig()
    var mTmpScheduleUploadFileConfig:List<TmpScheduleUploadFileConfig>? = null

    //腾讯存储COS配置
    var mCosSecretId:String = ""
    var mCosSecretKey:String = ""
    var mCosRegion:String = ""
    var mCosBucket:String = ""
    var mFileUploadServer:FileUploadServer = FileUploadServer.COS

    var mLogWriteModeConfig:LogWriteModeConfig = LogWriteModeConfig()

    constructor(builder: Builder){
        //mMaxFile = builder.maxFile
        //mDay = builder.day
        mPathPath = builder.pathPath
        mContent = builder.context?:throw java.lang.RuntimeException("config context == null")
        mIsPrintLog = builder.isPrintLog
        loggers = builder.loggers
        mCustomHost = builder.customHost
        mUploadPublicKey = builder.uploadPublicKey
        mUploadPrivateKey = builder.uploadPrivateKey
        mBucketName = builder.bucketName
        mUniqueDeviceId = builder.uniqueDeviceId
        mIsUploadLog = builder.isUploadLog
        mIsSendLog = builder.isSendLog
        mSendUrl = builder.sendUrl
        mDefaultSendBodyParamFun = builder.defaultSendBodyParamFun
        mStorageDays = builder.storageDays
        mLowMemoryByte = builder.lowMemoryByte
        mIsDebug = builder.isDebug
        mIsOpenSimpleCacheLogUpload = builder.isOpenSimpleCacheLogUpload
        mUploadScheduleConfig = builder.uploadScheduleConfig
        mTmpScheduleUploadFileConfig = builder.tmpScheduleUploadFileConfig
        mCosSecretId = builder.cosSecretId
        mCosSecretKey = builder.cosSecretKey
        mCosRegion = builder.cosRegion
        mCosBucket = builder.cosBucket
        mFileUploadServer = builder.fileUploadServer
        mLogWriteModeConfig = builder.logWriteModeConfig
    }

    /**
     * 获取本地储存log的文件路径
     */
    fun getLocalFileDir():File{
        var file: File
        if(mPathPath.isNotEmpty()){
            file = File(mPathPath,"${PARENT_DIR}/${DateUtil.getCurrentYMString()}/${DateUtil.getCurrentYMDString()}")
        }else{
            file = File(mContent.getExternalFilesDir(null),"${PARENT_DIR}/${DateUtil.getCurrentYMString()}/${DateUtil.getCurrentYMDString()}")
        }
        if(!file.exists()){
            val success = file.mkdirs()
            if(!success && mIsDebug){
                Log.e("Logc","创建本地储存log文件夹失败：${file.absolutePath}")
            }
        }
        return file
    }

    fun getParentFileDir():File{
        var file: File
        if(mPathPath.isNotEmpty()){
            file = File(mPathPath,PARENT_DIR)
        }else{
            file = File(mContent.getExternalFilesDir(null),PARENT_DIR)
        }
        return file
    }

    fun getCloudParentDir():String{
        return getCloudParentDir(System.currentTimeMillis())
    }

    fun getCloudParentDir(timestamp:Long):String{
        return mCloudParentDir.ifBlank {
            "$PARENT_DIR/${DateUtil.getCurrentYMString(timestamp)}/${DateUtil.getCurrentYMDString(timestamp)}/${mUniqueDeviceId.ifBlank { DeviceUtil.getUniqueId(mContent) }}"
        }
    }

    fun getCloudParentDir(file: File,parseFileCreateTime:Boolean = true,parsePreParentPathName:Boolean = false):String{
        val timestamp = FileUtil.getFileNameToTimestamp(file,parseFileCreateTime,parsePreParentPathName)
        if(timestamp != null && timestamp > 0){
            return getCloudParentDir(timestamp)
        }
        return getCloudParentDir()
    }

    fun getStorageDayMillisecond():Long = DAYS * mStorageDays

    fun getTmpSimpleCacheLogParentDir():String{
        return "logc/tmp/simple_cache_log/${mUniqueDeviceId.ifBlank { DeviceUtil.getUniqueId(mContent) }}"
    }

    fun getExternalFileLogParentDir(file: File):String{
        val timestamp = FileUtil.getFileNameToTimestamp(file,
            parseFileCreateTime = false,
            parsePreParentPathName = true
        )?:System.currentTimeMillis()
        return "logc/tmp/file_log/${DateUtil.getCurrentYMString(timestamp)}/${DateUtil.getCurrentYMDString(timestamp)}/${mUniqueDeviceId.ifBlank { DeviceUtil.getUniqueId(mContent) }}"
    }

    fun getLocalSimpleCacheLogPathParentDir():File{
        return File(mContent.externalCacheDir,"log")
    }

    class Builder{

        //var maxFile = DEFAULT_FILE_SIZE
        //var day = DEFAULT_MONTH

        var isPrintLog:Boolean = false //是否打印
        var isUploadLog:Boolean = false //是否上传
        var isSendLog:Boolean = false //是否上传

        var loggers: List<Logger>? = null

        var pathPath :String = "" //本地日志存储父路径，默认存在外部缓存文件中

        var context:Context? = null

        var customHost:String = "" //上传服务器的路径
        var uploadPublicKey:String = ""
        var uploadPrivateKey:String = ""
        var bucketName:String = ""
        var uniqueDeviceId:String = "" //设备唯一序列码

        var sendUrl:String = "" //上传后台的url
        var defaultSendBodyParamFun:(()->Map<String,Any?>?)? = null

        var storageDays:Long = 30
        var lowMemoryByte:Long = 200 * M

        var isDebug:Boolean = false

        var isOpenSimpleCacheLogUpload = false

        var uploadScheduleConfig = UploadScheduleConfig()
        var tmpScheduleUploadFileConfig:List<TmpScheduleUploadFileConfig>? = null

        var cosSecretId:String = ""
        var cosSecretKey:String = ""
        var cosRegion:String = ""
        var cosBucket:String = ""
        var fileUploadServer:FileUploadServer = FileUploadServer.COS

        var logWriteModeConfig:LogWriteModeConfig = LogWriteModeConfig()

        fun build() = LogcConfig(this)
    }

    /**
     * 日志上传的定时策略配置
     */
    data class UploadScheduleConfig(val time: Long = 2, val unit: TimeUnit = TimeUnit.HOURS,val strategy:UploadStrategy = UploadStrategy.ThreadScheduled){

        enum class UploadStrategy{
            WorkManager,//使用WorkManager实现定时任务
            ThreadScheduled //使用线程池实现定时任务
        }
    }

    /**
     * 非日志文件定时上传配置
     */
    data class TmpScheduleUploadFileConfig(
        val subToken:String,//唯一识别任务
        val file:List<File>,//需要上传文件
        val cloudPathName:List<String>,//远程服务器存放的路径，存在logc/tmp/路径下
        val time: Long = 2,//循环定时时间
        val unit: TimeUnit = TimeUnit.HOURS,//时间单位
        val isNow:Boolean,//是否立即执行
        val permissionCallback:(callback:(Boolean)->Unit)->Unit,//权限回调，成功才会设置定时任务
        val enabled:Boolean,//是否开启定时上传
    )

    enum class FileUploadServer{
        UC, //UC储存服务
        COS//腾讯COS储存服务
    }

    data class LogWriteModeConfig(
        val mode:Mode = Mode.IO,
        //以下是MMAP配置
        val encryptMode: EncryptMode? = null,
        val serverPublicKey:String? = null,
        val serverPrivateKey:String? = null,
        val mmapSafe:Boolean = true
    ){
        enum class Mode{
            IO, //传统IO记录
            MMAP //MMAP记录
        }

        enum class EncryptMode(private val value:Int){
            None(1),Fixed(2), AES(3);

            fun value() = value

            fun getClogEncryptMode(): Clog.EncryptMode{
                return when(value){
                    None.value() -> Clog.EncryptMode.None
                    Fixed.value() -> Clog.EncryptMode.Fixed
                    AES.value() -> Clog.EncryptMode.AES
                    else -> Clog.EncryptMode.Fixed
                }
            }

        }

        fun isIO() = mode == Mode.IO

        fun isMMAP() = mode == Mode.MMAP
    }

}